关于bash:Linux:为给定的文件夹和内容计算单个哈希?

您所在的位置:网站首页 md5sum 文件名 关于bash:Linux:为给定的文件夹和内容计算单个哈希?

关于bash:Linux:为给定的文件夹和内容计算单个哈希?

2023-08-13 22:59| 来源: 网络整理| 查看: 265

当然,必须有一种方法可以轻松地做到这一点!

我已经尝试使用Linux命令行应用程序,例如sha1sum和md5sum,但它们似乎只能计算单个文件的哈希值并输出哈希值列表,每个文件一个。

我需要为文件夹的整个内容生成一个哈希(不仅仅是文件名)。

我想做类似的事情

1sha1sum /folder/of/stuff > singlehashvalue

编辑:澄清一下,我的文件位于目录树中的多个级别,它们并不都位于同一根文件夹中。

相关讨论 通过全部内容,您是指目录中所有文件的逻辑数据还是到达根哈希时的元数据? 由于用例的选择标准相当广泛,因此我在我的回答中尝试解决一些实际的问题。

一种可能的方式是:

1sha1sum path/to/folder/* | sha1sum

如果有整个目录树,则最好使用find和xargs。一种可能的命令是

1find path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

最后,如果您还需要考虑权限和空目录:

1234(find path/to/folder -type f -print0  | sort -z | xargs -0 sha1sum;  find path/to/folder \( -type f -o -type d \) -print0 | sort -z | \    xargs -0 stat -c '%n %a') \ | sha1sum

stat的参数将导致它打印文件的名称,后跟其八进制权限。这两个查找将一个接一个地运行,从而导致磁盘IO数量翻倍,第一个查找所有文件名并校验和内容,第二个查找所有文件和目录名,打印名称和方式。然后,将对"文件名和校验和"列表以及"具有权限的名称和目录"列表进行校验和,以得到较小的校验和。

相关讨论 如果您在第一个sha1sum之后进行排序,则文件名中的LF应该不会造成损害。 编辑。使用-z选项可以对0个定界列表进行排序。 并且不要忘记设置LC_ALL = POSIX,因此各种工具都会创建与语言环境无关的输出。 我发现猫| sha1sum要比sha1sum快得多| sha1sum。 YMMV,请在您的系统上尝试以下每种方法:time find path / to / folder -type f -print0 |排序-z | xargs -0 sha1sum | sha1sum;时间查找路径/到/文件夹-type f -print0 |排序-z | xargs -0猫| sha1sum @RichardBronosky-假设我们有两个文件,A和B。A包含" foo",B包含" bar was here"。使用您的方法,我们将无法将其与两个文件C和D分开,其中C包含" foobar",D包含"在这里"。通过分别散列每个文件,然后散列所有"文件名散列"对,我们可以看到区别。 要使该工作与目录路径无关(例如,当您要比较两个不同文件夹的哈希值时),您需要使用相对路径并更改为适当的目录,因为这些路径包括在最终的哈希中: @robbles这是正确的,为什么我没有在pathtofolder位上放置初始的。 您还可以在FreeBSD上让您的hashtool只打印出哈希,例如:xargs -0 sha256 -q(此外,在您的anwser中,您可能想提请注意以下事实:(绝对)文件名是与哈希一起打印出来的) @hopla相对路径贯穿整个路径,而不仅仅是最后一个示例。 更清楚的是:)我还认为使用相对路径比-q选项更好,因为在最终的哈希中还考虑了所有文件名,避免了发生哈希冲突的问题。 可以处理大量文件吗? @JasonS定义"大"吗?您正在查看纯数据量中的大致线性运行时(由sha1sum或等效的散列消耗)。您正在(大致)从find看线性性能。排序可能是o(n log n),文件"文件数"为n。在" log n"开始显着增长之前,时间将由磁盘带宽决定。 Id挥舞着一只手,Id表示您"可以处理成千上万个文件"。在某些时候,要排序的每个文件哈希列表可能需要洒到磁盘上,因此时间复杂度曲线上将是一团糟。 不,我担心大型命令行; xargs一次调用sha1sum,对吗?命令行大小有限制吗? @JasonS啊,不,xargs的原因是它可以智能地将输入的"要哈希的文件名"流从find拆分成合适的块(默认值是um,有点低(基本上,它取决于系统,但是默认值应该取决于系统)永远安全)。 尽管此命令在某些用例下看起来很好用,但它似乎并未包括可能相关的详细信息,例如目录名和文件权限。我肯定有不止一种方式给猫皮。 @BinaryPhile是正确的,但不是最初要求的问题。但是,所有包含内容的目录都将其名称作为最终哈希的一部分(它们是文件名的一部分)。可能包括权限,但需要(一些)思考,因为普通的" ls -l"将包括(可能)不相关的日期和时间信息。 因此,这没有捕获权限? 这也不会捕获空目录。 @CMCDragonkai不,它仅捕获文件内容,请确保遵守文件边界。如果还希望包括权限和空目录,则可以添加类似find pathtofolder \( -type f -o -type d \) -print0 | sort -z | xargs stat -c"%n %a"的内容。让我编辑问题... 为了解决Mac和RHEL 5.x服务器之间的排序算法差异,我不得不稍微修改一下命令:find .folder -type f -print0 | xargs -0 sha1sum | sort -df | sha1sum 小心查找。在find somepathdir1 -type f ...和find someotherpathdir2 -type f ...上运行脚本将返回不同的校验和,即使dir1和dir2的内容相同。您需要先cd somepathdir1,然后再调用find . -type f ...

使用文件系统入侵检测工具,如助手。

哈希目录的tar球:

tar cvf - /path/to/folder | sha1sum

自己编写一些东西,例如vatine的oneliner:

find /path/to/folder -type f -print0 | sort -z | xargs -0 sha1sum | sha1sum

相关讨论 +1为tar解决方案。那是最快的,但是放弃v。冗长只会减慢它的速度。 请注意,当您比较文件时,tar解决方案假定文件的顺序相同。它们是否将取决于进行比较时文件所驻留的文件系统。 git哈希不适用于此目的,因为文件内容只是其输入的一部分。即使对于分支的初始提交,哈希也受提交消息和提交元数据的影响,例如提交时间。如果多次提交相同的目录结构,则每次将获得不同的哈希,因此生成的哈希不适合仅通过发送哈希来确定两个目录是否是彼此的精确副本。 @Zoltan,如果您使用树形哈希而不是提交哈希,则git哈希非常好。 @hobbs答案最初表示为"提交哈希",这当然不适合此目的。树形哈希听起来像是一个更好的候选者,但是仍然可能存在隐藏的陷阱。我想到的是在某些文件上设置可执行位会更改树的哈希值。您必须先提交git config --local core.fileMode false才能避免这种情况。我不知道是否还有这样的警告。

你可以做tar -c /path/to/folder | sha1sum

相关讨论 如果要在另一台计算机上复制该校验和,tar可能不是一个好选择,因为该格式似乎有歧义的余地并且存在于许多版本中,因此另一台计算机上的tar可能会从同一文件产生不同的输出。 尽管存在缓慢的问题,但如果您关心文件的内容,权限等而不是修改时间,则可以像下面这样添加--mtime选项:tar -c pathtofolder --mtime="1970-01-01" | sha1sum。 @ S.Lott如果目录很大,我的意思是如果目录太大,则将其压缩并获取md5会花费更多时间

如果您只想检查文件夹中的某些内容是否发生了变化,我建议您这样做:

1ls -alR --full-time /folder/of/stuff | sha1sum

它只会给您ls输出的哈希,其中包含文件夹,子文件夹,它们的文件,它们的时间戳,大小和权限。确定是否已更改的几乎所有内容。

请注意,该命令不会为每个文件生成哈希,但这就是为什么它比使用find更快的原因。

相关讨论 由于解决方案的简单性,我不确定为什么没有更多的支持。谁能解释为什么这行不通? 我认为这不是理想的,因为生成的哈希将基于文件所有者,日期格式设置等。 可以自定义ls命令以输出所需的任何内容。您可以将-l替换为-gG以省略组和所有者。您可以使用--time-style选项更改日期格式。基本上检查一下ls手册页,看看什么适合您的需求。 @DaveC,因为它几乎没有用。如果要比较文件名,则直接比较它们。他们没有那么大。 @Navin从这个问题尚不清楚是否需要散列文件内容或检测树中的更改。每种情况都有其用途。例如,在内核树中存储45K文件名不如单个哈希值实用。 ls -lAgGR --block-size = 1 --time-style = +%s | sha1sum对我有用

如果您只想散列文件的内容,而忽略文件名,则可以使用

1cat $FILES | md5sum

计算哈希值时,请确保文件顺序相同:

1cat $(echo $FILES | sort) | md5sum

但是,文件列表中不能包含目录。

相关讨论 将一个文件的末尾按字母顺序移动到紧随其后的文件的开头不会影响哈希,但应该会影响哈希。文件分隔符或文件长度将需要包含在哈希中。

强大而干净的方法 首先,不要浪费可用内存!散列文件而不是整个文件。 针对不同需求/目的的不同方法(以下全部内容或选择适用的方法): 仅散列目录树中所有条目的条目名称 散列所有条目的文件内容(保留元数据,如inode编号,ctime,atime,mtime,size等,您便会明白) 对于符号链接,其内容为引用名称。散列或选择跳过 哈希条目内容时遵循或不遵循(解析名称)符号链接 如果是目录,则其内容仅是目录条目。在递归遍历时,它们最终将被散列,但是是否应该对该级别的目录条目名称进行散列以标记该目录?在需要散列以快速识别更改而不必深入遍历以散列内容的用例中很有用。一个例子是文件的名称更改,但其余内容保持不变,并且它们都是相当大的文件 妥善处理大文件(再次注意RAM) 处理非常深的目录树(注意打开的文件描述符) 处理非标准文件名 如何处理套接字,管道/ FIFO,块设备,char设备等文件?还必须对它们进行哈希处理吗? 在遍历时不要更新任何条目的访问时间,因为这在某些用例中会产生副作用并且适得其反(直观吗?)。

这就是我的头等大事,任何花了一些时间从事这一工作的人实际上都会抓到其他陷阱和死角。

这是一个非常轻巧的工具,可以解决大多数情况,可能有些麻烦,但很有帮助。

dtreetrawl的示例用法和输出。

123456789101112131415161718Usage:   dtreetrawl [OPTION...]"/trawl/me" [path2,...] Help Options:   -h, --help                Show help options Application Options:   -t, --terse               Produce a terse output; parsable.   -j, --json                Output as JSON   -d, --delim=:             Character or string delimiter/separator for terse output(default ':')   -l, --max-level=N         Do not traverse tree beyond N level(s)   --hash                    Enable hashing(default is MD5).   -c, --checksum=md5        Valid hashing algorithms: md5, sha1, sha256, sha512.   -R, --only-root-hash      Output only the root hash. Blank line if --hash is not set   -N, --no-name-hash        Exclude path name while calculating the root checksum   -F, --no-content-hash     Do not hash the contents of the file   -s, --hash-symlink        Include symbolic links' referent name while calculating the root checksum   -e, --hash-dirent         Include hash of directory entries while calculating root checksum

一段人类友好的输出:

12345678910111213141516171819202122232425262728293031323334353637... ... //clipped ... /home/lab/linux-4.14-rc8/CREDITS         Base name                    : CREDITS         Level                        : 1         Type                         : regular file         Referent name                :         File size                    : 98443 bytes         I-node number                : 290850         No. directory entries        : 0         Permission (octal)           : 0644         Link count                   : 1         Ownership                    : UID=0, GID=0         Preferred I/O block size     : 4096 bytes         Blocks allocated             : 200         Last status change           : Tue, 21 Nov 17 21:28:18 +0530         Last file access             : Thu, 28 Dec 17 00:53:27 +0530         Last file modification       : Tue, 21 Nov 17 21:28:18 +0530         Hash                         : 9f0312d130016d103aa5fc9d16a2437e Stats for /home/lab/linux-4.14-rc8:         Elapsed time     : 1.305767 s         Start time       : Sun, 07 Jan 18 03:42:39 +0530         Root hash        : 434e93111ad6f9335bb4954bc8f4eca4         Hash type        : md5         Depth            : 8         Total,                 size           : 66850916 bytes                 entries        : 12484                 directories    : 763                 regular files  : 11715                 symlinks       : 6                 block devices  : 0                 char devices   : 0                 sockets        : 0                 FIFOs/pipes    : 0

有一个python脚本:

http://code.activestate.com/recipes/576973-getting-the-sha-1-or-md5-hash-of-a-directory/

如果您更改文件名而不更改其字母顺序,则哈希脚本将无法检测到它。但是,如果您更改文件的顺序或任何文件的内容,则运行脚本将为您提供与以前不同的哈希值。

我编写了一个Groovy脚本来做到这一点:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748import java.security.MessageDigest public static String generateDigest(File file, String digest, int paddedLength){     MessageDigest md = MessageDigest.getInstance(digest)     md.reset()     def files = []     def directories = []     if(file.isDirectory()){         file.eachFileRecurse(){sf ->             if(sf.isFile()){                 files.add(sf)             }             else{                 directories.add(file.toURI().relativize(sf.toURI()).toString())             }         }     }     else if(file.isFile()){         files.add(file)     }     files.sort({a, b -> return a.getAbsolutePath() b.getAbsolutePath()})     directories.sort()     files.each(){f ->         println file.toURI().relativize(f.toURI()).toString()         f.withInputStream(){is ->             byte[] buffer = new byte[8192]             int read = 0             while((read = is.read(buffer)) > 0){                 md.update(buffer, 0, read)             }         }     }     directories.each(){d ->         println d         md.update(d.getBytes())     }     byte[] digestBytes = md.digest()     BigInteger bigInt = new BigInteger(1, digestBytes)     return bigInt.toString(16).padLeft(paddedLength, '0') } println" ${generateDigest(new File(args[0]), 'SHA-256', 64)}"

您可以自定义用法,以避免打印每个文件,更改消息摘要,删除目录哈希等。我已经针对NIST测试数据对其进行了测试,并且可以正常工作。 http://www.nsrl.nist.gov/testdata/

123456789101112131415161718192021gary-macbook:Scripts garypaduana$ groovy dirHash.groovy /Users/garypaduana/.config .DS_Store configstore/bower-github.yml configstore/insight-bower.json configstore/update-notifier-bower.json filezilla/filezilla.xml filezilla/layout.xml filezilla/lockfile filezilla/queue.sqlite3 filezilla/recentservers.xml filezilla/sitemanager.xml gtk-2.0/gtkfilechooser.ini a/ configstore/ filezilla/ gtk-2.0/ lftp/ menus/ menus/applications-merged/ 79de5e583734ca40ff651a3d9a54d106b52e94f1f8c2cd7133ca3bbddc0c6758

实现此目的的另一个工具:

http://md5deep.sourceforge.net/

听起来像:md5sum,但也递归,还有其他功能。

相关讨论 尽管此链接可以回答问题,但最好在此处包括答案的基本部分,并提供链接以供参考。如果链接的页面发生更改,仅链接的答案可能会失效。

我将通过sort将单个文件的结果通过管道传输(以防止仅重新排序文件以更改哈希值),将其转换为md5sum或sha1sum,无论您选择哪种方式。

尝试分两个步骤进行:

为文件夹中的所有文件创建带有哈希的文件 散列此文件

像这样:

12# for FILE in `find /folder/of/stuff -type f | sort`; do sha1sum $FILE >> hashes; done # sha1sum hashes

或一次完成所有操作:

1# cat `find /folder/of/stuff -type f | sort` | sha1sum 相关讨论 当名称中有空格时,for F in find ... ...不起作用(如今始终如此)。

如果这是一个git repo,并且您想忽略.gitignore中的任何文件,则可能要使用以下命令:

1git ls-files | xargs sha256sum | cut -d"" -f1 | sha256sum | cut -d"" -f1

这对我来说很好。

这是Python 3中的一个简单,简短的变体,适用于小型文件(例如,源树或类似的东西,其中每个文件都可以轻松地放入RAM),并根据其他解决方案的想法忽略了空目录:

1234567import os, hashlib def hash_for_directory(path, hashfunc=hashlib.sha1):                                                                                                 filenames = sorted(os.path.join(dp, fn) for dp, _, fns in os.walk(path) for fn in fns)             index = ' '.join('{}={}'.format(os.path.relpath(fn, path), hashfunc(open(fn, 'rb').read()).hexdigest()) for fn in filenames)                   return hashfunc(index.encode('utf-8')).hexdigest()

它是这样的:

递归查找目录中的所有文件并按名称排序 计算每个文件的哈希值(默认值:SHA-1)(将整个文件读入内存) 用" filename = hash"行创建文本索引 将该索引重新编码为UTF-8字节字符串,并对其进行哈希处理

如果SHA-1不是您喜欢的茶,则可以传入另一个哈希函数作为第二个参数。

我必须检查整个目录以进行文件更改。

但要排除时间戳,目录所有权。

目标是在文件相同的情况下,在任何地方都获得相同的总和。

包括托管到其他计算机中,除了文件以外的任何东西,或对其进行更改。

1md5sum * | md5sum | cut -d' ' -f1

它按文件生成一个哈希列表,然后将这些哈希串联为一个。

这比tar方法快得多。

为了更好地保护我们的哈希,我们可以在同一食谱上使用sha512sum。

1sha512sum * | sha512sum | cut -d' ' -f1

使用sha512sum的哈希值在任何地方都相同,但是没有已知的方法可以将其反转。

您可以sha1sum生成哈希值列表,然后再次sha1sum生成该列表,这取决于您要完成的目标。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3